/*  REVIEWED_FOR_64BIT=YES  */
// Copyright (C) 1997, All Rights Reserved, by
// EMC Corporation, Hopkinton MA.
//
// This software is furnished under a license and may be used and copied
// only  in  accordance  with  the  terms  of such  license and with the
// inclusion of the above copyright notice. This software or  any  other
// copies thereof may not be provided or otherwise made available to any
// other person. No title to and ownership of  the  software  is  hereby
// transferred.
//
// The information in this software is subject to change without  notice
// and  should  not be  construed  as  a commitment by EMC Corporation.
//
// EMC assumes no responsibility for the use or  reliability  of its
// software on equipment which is not supplied by EMC.
 
// FACILITY:
// 
// Dart Memory Log facility
//
// ABSTRACT:
//
// Allows logging messages to the memory buffer
//
// AUTHORS:
//
// Boris Farizon
//
// CREATION DATE:
// 
// Aug 29, 1997
//
// MODIFICATION HISTORY:
//
//                     TABLE OF CONTENTS:
//
// This file contains definition of the following classes:
// logObject_Memory
// logObject_MemoryPrivate
// outputWriter_MemBuffer 
//
 
#if !defined(_LOGMEM_HXX_)
#define _LOGMEM_HXX_

#if !defined(lint) && defined(INCLUDE_RCSID)
/*  static char logmem_hxx_rcsid[] =
  "$Id: logmem.hxx,v 11.11 2010/02/20 00:53:37 williw Exp $"
  "$Source: /usr/local/cvs/archive/Dart/server/src/kernel/min_kernel/include/logmem.hxx,v $"; */
#endif

#include <stdiolib.h>         // definition of FILE class
#include <logger.hxx>         // definition of logObject class
#include <logmem_buffer.hxx>  // definition of cMemoryBuffer class
#include <m_memfrm.hxx>       // definition of Memory_PageSize constant
#include <sthread.hxx>        // definition of Sthread class
 
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//  Definition of the class outputWriter_MemBuffer
//
//  Derived from FILE, this class wraps cMemoryBuffer with traditional,
//  file-style interface. It allows file output functions to be used 
//  with the new destination -- memory buffer
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
class outputWriter_MemBuffer : public FILE {
  public:
    outputWriter_MemBuffer(cMemoryBuffer* pMemBuffer = (cMemoryBuffer* )0);
    // Constructor (receives a pointer to the buffer to write to
    // as a parameter)

    ~outputWriter_MemBuffer();
    // Destructor

  public:
    // FILE interface overriden function:
    //
    virtual int putc(int c);
    //  write c as the next character to this.

    virtual ssize_t write(const void *src, size_t nbytes);
    // The write() function attempts to write nbytes bytes from the buffer
    // pointed to by src to this. Returns number of bytes written.

    void initialize(cMemoryBuffer* pMemBuff); 
    // assigns a buffer to write output to 
    
    int nextIndex(void); 
    // returns the index of the buffer to which the next character is 
    // to be written 

  protected:      
    boolean_t wasInitialized(void) { return m_Initialized; }

  protected:
    cMemoryBuffer* m_Buffer; 
    boolean_t m_Initialized;
    int curCount;
};
     
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//  Definition of the class logObject_Memory
//  which allows accumulation of messages up to pre-defined capacity. 
//  If more messages arrive then the capacity allows,
//  then data is either truncated or wrapped around, depending on the 
//  flag given in the constructor
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++           
class logObject_Memory:	public logObject {
  public:
    logObject_Memory(
     int nBytes = 0, 
     // log capacity: if <= 0, then the default (Memory_PageSize) is used
     boolean_t boolTrancateExtraMessages = TRUE,
     // truncation mode: if == TRUE, all data that exceeds log capacity
     // will be truncated; otherwise, extra mesages will be 'wrapped around',
     // overriding the oldest ones
     char* extrnBuffer = 0,
     // if external buffer is supplied (that is, extrnBuffer != 0 && nBytes > 0)
     // it will be used to actually keep messages being logged
     int msgDelimiterCh = EOF);
     // delimiter to be placed between messages to separate them
     // no delimiter by default

    // Destructor
    virtual ~logObject_Memory();

    // Standard logObject interface
    // (see logger.hxx for details)
    // 
    void logmsg (CCMD_Msg* msg);
    void logmsg (CCMD_Id, const char* fmt, va_list args);
    void logmsg (logFacility f, logSeverity s, msgid_t id);
    void logvmsg (logFacility f, logSeverity s, const char* fmt, va_list args);			  
    void logvmsg (logFacility f, logSeverity s, msgid_t id, const char* fmt, va_list args);			  
    void logOpaque (logFacility f, logSeverity s, u_long len, void* paramp);  			    
    int print (const char* fmt, va_list argp);
    void print (const char* fmt, ...);
    void flushLog (boolean_t doWait = TRUE) { /* void */ }
    boolean_t isReady()	{ return TRUE; }
    void addClient (logFacility f, logSeverity s) { /* void */ }  

    // Class-specific public member functions
    //        
    int getLogCapacity(void) const;
    // returns capacity of this object in bytes 

    int getDataLength(void) const;
    // returns actual length of the recorded data in bytes

    int getLogContents(char* destBuffer, int nBytes = 0, boolean_t leastRecent = TRUE) const;
    // Copies the log's contents to the caller-provided buffer.
    // If nBytes == 0, then all contents will be copied, regardless of leastRecent flag.
    // If leastRecent == TRUE, then the least recent (leading) nBytes
    // will be copied. 
    // Otherwise, if leastRecent == FALSE, then the most recent (trailing)
    // nBytes bytes will be copied.

    boolean_t shouldTruncate(void) const;
    // returns TRUE if extra messages should be truncated; otherwise, 
    // returns FALSE

    boolean_t isTruncated(void) const;
    // returns TRUE if the data in the buffer is actually truncated;
    // returns FALSE otherwise

    boolean_t isFull(void) const;
    // returns TRUE if the buffer is completely filled up; otherwise,
    // returns FALSE

    void resetLog(void);
    // empties the log object, so logging can be re-started from the beginning

    // Class-specific protected and private member functions and data members
    //
  protected:    
    enum { MemLog_DefaultCapacity = Memory_PageSize };
    // default log object size

    virtual boolean_t canLog(void);
    // returns TRUE if the next message can be logged; otherwise,
    // returns FALSE
    
    void beginMessage(void);
    // forestalls logging of a message to this object

    void endMessage(void);
    // finalizes logging of a message to this object

    const char* getFacilityName(logFacility aFacility) const;
    // converts facility id to it's symbolic name

    void printFacilityAndSeverity(logFacility, logSeverity);
    // logs facility and severity pair

    void printMsgId(msgid_t);
    // logs message id

    boolean_t shouldUseDelimiter(void);
    // returns TRUE if delimiter char was defined and should be used;
    // otherwise, returns FALSE

    void swapBuffers(char* newBuf);
    // replace existing buffer with newBuf

    // lock and unlock for output (AR 99503)
    void inUseLock();
    void inUseUnLock();

    int                        m_LogCapacity;      // log object capacity in bytes
    cMemoryBuffer              m_Buffer;           // buffer to log messages into
    outputWriter_MemBuffer     m_BuffWriter;       // buffer-FILE adapter    
    char 		       m_MsgDelimiter;     // message delimiter char
    boolean_t                  m_boolTruncate;     // truncate/wrap around flag 
    boolean_t                  m_boolUseDelimiter; // use / not use delimiter flag
    boolean_t                  m_inUse_at_inlv;
    Sthread_MutexSplHigh       m_inUseHigh;
};
 
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//  Definition of the class logObject_MemoryPrivate
//  Allows logging of messages into the memory buffer
//  on the per-thread basis. That is, only owner
//  of a logObject_MemoryPrivate object is allowed 
//  to log messages to it.
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++             
class logObject_MemoryPrivate:	public logObject_Memory	{
public:
    // Constructor
    logObject_MemoryPrivate(
                            Sthread * pOwnerThread, // pointer to the owner thread object
			    // rest of the parameters as described in logObject_Memory ctor 
                            int nBytes = 0,
			    boolean_t boolTrancateExtraMessages = TRUE, 
			    char* extrnBuffer = 0,
			    int msgDelimiterCh = EOF // no delimiter by default
                            );
    // Destructor
    virtual ~logObject_MemoryPrivate();

    //
    // Class-specific protected and private member functions and data members
    //
protected:    
    virtual boolean_t canLog(void);
    // in addition to what is done by the base class's canLog(),
    // also checks access permission of the caller

    boolean_t isAccessPermitted(void) const;
    // checks if the caller has a permission to log messages to this object

private:
    struct {
      Sthread* threadPtr;
      unsigned int threadTag;
    } m_OwnerInfo;
    // structure that holds information about the thread that owns
    // this object
};
 
//////////////////////////////////////////////////////////////////////////////
////  INLINES  (logObject_Memory)
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//// getFacilityName(), printFacilityAndSeverity(), printMsgId()
//// are auxiliry functions invented to prevent replications of similar code
//////////////////////////////////////////////////////////////////////////////
inline 
const char* logObject_Memory::getFacilityName(logFacility aFacility) const
{
   // convert facility Id to it's name
   return ( logFacilityName[aFacility] );
}
inline 
void logObject_Memory::printFacilityAndSeverity(logFacility aFacility, logSeverity aSeverity)
{
  if ((aFacility != LOG_NO_PREFIX) && (aFacility != LOG_STATS)) 
  {
    m_BuffWriter.print("%s:%2d: ", getFacilityName(aFacility), aSeverity); 
  }  
}
inline
void logObject_Memory::printMsgId(msgid_t aMsgId)
{
  m_BuffWriter.print("%8d\n", aMsgId);
}

//////////////////////////////////////////////////////////////////////////////
//// int logObject_Memory::getLogCapacity(void)
////
//// DESCRIPTION
//// Returns the log object capacity in bytes 
//////////////////////////////////////////////////////////////////////////////  
inline
int logObject_Memory::getLogCapacity(void) const
{
  return m_LogCapacity;
}
 
//////////////////////////////////////////////////////////////////////////////
//// int logObject_Memory::getDataLength(void)
////
//// DESCRIPTION
//// Returns the length of logged data in bytes
//////////////////////////////////////////////////////////////////////////////
inline 
int logObject_Memory::getDataLength(void) const
{
  return m_Buffer.getCharCount();
}
//////////////////////////////////////////////////////////////////////////////
//// int logObject_Memory::getLogContents(char* destBuff, int nBytes)
////
//// DESCRIPTION
//// Copies nBytes of the oldest data to the caller's buffer
//// Returns the number of bytes actually copied
//////////////////////////////////////////////////////////////////////////////  
inline 
int logObject_Memory::getLogContents(char* destBuff, int nBytes, boolean_t leastRecent) const
{
  if ( (nBytes == 0) || (leastRecent) ) // entire contents or least recent chunk required
    return m_Buffer.getLeastRecentChunk(destBuff, nBytes);

  return m_Buffer.getMostRecentChunk (destBuff, nBytes); // most recent chunk required
}
//////////////////////////////////////////////////////////////////////////////
//// void logObject_Memory::beginMessage(void)
////
//// DESCRIPTION
//// Forestalls logging of a message to this object
//////////////////////////////////////////////////////////////////////////////
inline
void logObject_Memory::beginMessage(void)
{
  // does nothing currently
}

//////////////////////////////////////////////////////////////////////////////
//// void logObject_Memory::endMessage(void)
////
//// DESCRIPTION
//// Finalizes logging of a message to this object
//////////////////////////////////////////////////////////////////////////////
inline
void logObject_Memory::endMessage(void)
{
  // print the delimiter char at the end of a message
  if ( shouldUseDelimiter() )
    m_BuffWriter.putc( m_MsgDelimiter );
}
//////////////////////////////////////////////////////////////////////////////
//// inline boolean_t logObject_Memory::isFull(void)
////
//// DESCRIPTION
//// Returns TRUE if the object's buffer is full; otherwise, returns FALSE
//////////////////////////////////////////////////////////////////////////////
inline boolean_t logObject_Memory::isFull(void) const
{
  return m_Buffer.isFull();
}
//////////////////////////////////////////////////////////////////////////////
//// inline boolean_t logObject_Memory::shouldTruncate(void)
////
//// DESCRIPTION
//// Returns TRUE if the object was created to function in 'truncate' mode;
//// otherwise, returns FALSE
//////////////////////////////////////////////////////////////////////////////
inline boolean_t logObject_Memory::shouldTruncate(void) const
{
  return (m_boolTruncate);
}

//////////////////////////////////////////////////////////////////////////////
//// inline boolean_t logObject_Memory::isTruncated(void) const;
//// 
//// DESCRIPTION
////
//// Returns TRUE if the data in the buffer is actually truncated;
//// returns FALSE otherwise.
//////////////////////////////////////////////////////////////////////////////
inline boolean_t logObject_Memory::isTruncated(void) const
{
  return m_Buffer.isTruncated();
}

//////////////////////////////////////////////////////////////////////////////
//// void logObject_Memory::resetLog(void)
////
//// DESCRIPTION
//// Empties the memory buffer, so that the log object becomes empty, too
//// Logging then restarts from the beginning
//////////////////////////////////////////////////////////////////////////////
inline void logObject_Memory::resetLog(void)
{  
  m_BuffWriter.flush();
  m_Buffer.empty();
}
//////////////////////////////////////////////////////////////////////////////
//// inline boolean_t logObject_Memory::shouldUseDelimiter(void)
////
//// DESCRIPTION
//// Returns TRUE if delimiter char was defined and should be used;
//// otherwise, returns FALSE
////////////////////////////////////////////////////////////////////////////// 
inline boolean_t logObject_Memory::shouldUseDelimiter(void)
{
  return m_boolUseDelimiter;
}
 
//////////////////////////////////////////////////////////////////////////////
////  INLINES  (logObject_MemoryPrivate)
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//// boolean_t logObject_MemoryPrivate::isAccessPermitted(void)
////
//// DESCRIPTION
//// Validates access rights (to *this* object) of the calling thread
//////////////////////////////////////////////////////////////////////////////
inline
boolean_t logObject_MemoryPrivate::isAccessPermitted(void) const
{
  // check the equality of both the address and the tag between the owner
  // thread and the caller
  Sthread *pThread = Sthread::self();
  return ( (pThread == m_OwnerInfo.threadPtr) && (pThread->tag() == m_OwnerInfo.threadTag) );
} 

#endif // _LOGMEM_HXX_
